<div id="app">
    <p>{{title}}</p>
  </div>
  
  <script>
    // 传入信息，返回vnode，描述视图
    function h(tag, props, children) {
      return { tag, props, children }
    }
  
    // 1.基本结构
    const Vue = {
      // 扩展性
      createRenderer({ querySelector, insert, createElement, remove }) {
        // 返回渲染器
        return {
          createApp(options) {
            // 返回app实例
            return {
              mount(selector) {
                // console.log('mount!');
                // 1.找到宿主元素
                const parent = querySelector(selector)
  
                // 2.渲染页面
                if (!options.render) {
                  //   2.1处理template：编译
                  options.render = this.compile(parent.innerHTML)
                }
  
                // setup和其他选项
                if (options.setup) {
                  this.setupState = options.setup()
                }
                if (options.data) {
                  this.data = options.data()
                }
  
                // app.xxx
                const proxy = new Proxy(this, {
                  get(target, key) {
                    // 先从setup中取，如果取不到再从data中取
                    if (target.setupState && key in target.setupState) {
                      return target.setupState[key]
                    } else {
                      return target.data[key]
                    }
                  },
                  set(target, key, val) {
                    if (target.setupState && key in target.setupState) {
                      target.setupState[key] = val
                    } else {
                      target.data[key] = val
                    }
                  }
                })
  
                //   2.2用户直接编写render
                effect(() => {
                  // const el = options.render.call(proxy)
  
                  // // 3.追加到宿主
                  // parent.innerHTML = ''
                  // // parent.appendChild(el)
                  // insert(el, parent)x
                  console.log(effectStack[0])
                  const vnode = options.render.call(proxy)
                  // 转换vnode为dom
                  // 初始化创建整棵树
                  if (!this.isMounted) {
                    // 实现createElm，整体创建
                    const el = this.createElm(vnode)
                    parent.innerHTML = ''
                    insert(el, parent)
  
                    // init
                    this.isMounted = true
                  } else {
                    // todo: update
                    this.patch(this._vnode, vnode)
                  }
                  this._vnode = vnode
                })
                // this.update()
                // this.updata = effect
              },
              patch(n1, n2){
                const el = n2.el = n1.el
                // n1是老节点
                // n2是新节点
                // 1.更新：必须更新相同节点
                // 什么样的是相同节点
                // tag、key
                // sameVnode
                if (n1.tag === n2.tag && n1.key === n2.key) {
                  // update
                  // props todo
                  // children 
                  const oldCh = n1.children
                  const newCh = n2.children
  
                  if (typeof oldCh === 'string') {
                    if (typeof newCh === 'string') {
                      // text update
                      if (oldCh !== newCh) {
                        el.textContent = newCh
                      }
                    } else {
                      // 替换文本为一组子元素
                      // 清空再创建并追加
                      el.textContent = ''
                      newCh.forEach(child => insert(this.createElm(child), el))
                    }
                  } else {
                    if (typeof newCh === 'string') {
                      // 替换一组子元素为文本
                      el.textContent = newCh
                    } else {
                      // updateChildren
                      this.updateChildren(el, oldCh, newCh)
                    }
                  }
                } else {
                  // replace
                }
              },
              updateChildren(el, oldCh, newCh) {
                // 1.获取newCh和oldCh中较短的那一个
                const len = Math.min(oldCh.length, newCh.length)
                // 强制更新
                for (let i = 0; i < len; i++) {
                  this.patch(oldCh[i], newCh[i])
                }
  
                // 处理剩余元素
                // 新数组元素多
                if (newCh.length > oldCh.length) {
                  // 批量创建并追加
                  // 截取newCh中len后面的部分
                  newCh.slice(len).forEach(child => {
                    insert(this.createElm(child), el)
                  })
                }
                else if(newCh.length < oldCh.length) {
                  oldCh.slice(len).forEach(child => {
                    remove(child.el, el)
                  })
                }
              },
              createElm(vnode) {
                const { tag, props, children } = vnode
                // 遍历vnode，创建整棵树
                const el = createElement(tag)
                // 如果存在属性，就设置他们
                // el.setAttribute(key, val)
                // 递归
                // 判断children是字符串
                if (typeof children === 'string') {
                  el.textContent = children
                } else {
                  children.forEach(child => insert(this.createElm(child), el))
                }
                // vnode中要保存真实dom，以备未来更新使用
                vnode.el = el
                return el
              },
              compile(template) {
                // 返回一个render函数
                // parse -> ast
                // generate -> ast=>render
                return function render() {
                  if (Array.isArray(this.title)) {
                    return h('h3', null, this.title.map(s => h('p', null, s)))
                  } else {
                    return h('h3', null, this.title)
                  }
  
                  // return h('h3', null, this.title)
  
                  // return h('h3', null, [
                  //   h('p', null, this.title),
                  //   h('p', null, this.title),
                  //   h('p', null, this.title),
                  // ])
                }
              }
            }
          }
        }
      },
      createApp(options) {
        // 创建一个web平台特有渲染器
        const renderer = Vue.createRenderer({
          querySelector(sel) {
            return document.querySelector(sel)
          },
          insert(el, parent) {
            parent.appendChild(el)
          },
          createElement(tag) {
            return document.createElement(tag)
          },
          remove(el, parent) {
            parent.removeChild(el)
          }
        })
        return renderer.createApp(options)
      }
    }
  </script>
  <script>
    // 能够拦截用户对代理对象的访问
    // 从而在值发生变化的时候做出响应式
    function reactive(obj) {
      // vue2
      // Object.defineProperty(obj, {})
      // 缺点：性能、api、内部实现、数据结构支持
      return new Proxy(obj, {
        get(target, key) {
          console.log('get key:', key);
          // 建立依赖关系
          track(target, key)
          // console.log(targetMap);
          return target[key]
        },
        set(target, key, val) {
          console.log('set key:', key);
          target[key] = val
          // 获取依赖关系
          // 通知更新
          trigger(target, key)
          //  app.update()
        }
      })
    }
  
    // 建立映射关系：依赖dep - 组件更新函数
    // vue2： 1组件有watcher
    // vue3：创建map结构 
  
    // 调用effect，首先执行fn
    const effectStack = []
    function effect(fn) {
      const eff = function () {
        try {
          effectStack.push(eff)
          fn()
        } finally {
          effectStack.pop()
          console.log('pop')
          console.log(effectStack[0])
        }
      }

      // 立即调用一次
      eff()
  
      return eff
    }
  
    // { 
    //   target1: { key: [update1, update2]} ,
    //   target2: { key: [update1, update2]} ,
    // }
    // const targetMap = new WeakMap()
    const targetMap = {}
  
    // 建立target,key和effectStack中存储的副作用函数之间的关系
    function track(target, key) {
      const effect = effectStack[effectStack.length - 1]
      // 判断target为key的对象存在不存在
      let map = targetMap[target]
      // targetMap.get(target)
      if (!map) {
        // 首次get这个target
        map = targetMap[target] = {}
        // targetMap.set(target, {})
      }
  
      let deps = map[key]
      if (!deps) {
        deps = map[key] = []
        // deps = map[key] = new Set()
      }
      // 映射关系建立
      // deps.add(effect)
      if (deps.indexOf(effect) === -1) {
        deps.push(effect)
      }
    }
  
    // 从targetMap中获取target，key对应的依赖函数并执行他们
    function trigger(target, key) {
      const map = targetMap[target]
      if (map) {
        const deps = map[key]
  
        if (deps) {
          deps.forEach(dep => dep())
        }
      }
    }
  
    // const obj = reactive({foo: 'foo', bar: 'bar'})
    // effect(() => {
    //   // 触发响应式数据
    //   console.log(obj.foo);
    // })
    // effect(() => {
    //   console.log(obj.foo,obj.bar);
    // })
    // obj.foo = 'foo~~~~~~'
  
    const app = Vue.createApp({
      setup() {
        // created
        const state = reactive({
          title: 'vue3, hello!'.split('')
        })
        setTimeout(() => {
          state.title = 'hello, vue3!!!!!!!!!!!'.split('')
        }, 2000)
        return state
      }
    })
    app.mount('#app')
  </script>